/* ************************************************************************ */
/*                                                                          */
/*  Copyright (c)2005 Russell Weir | damonsbane _at_ gmail                  */
/*                                                                          */
/* This library is free software; you can redistribute it and/or            */
/* modify it under the terms of the GNU Lesser General Public               */
/* License as published by the Free Software Foundation; either             */
/* version 3.0 of the License, or (at your option) any later version.       */
/*                                                                          */
/* This library is distributed in the hope that it will be useful,          */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        */
/* Lesser General Public License or the LICENSE file for more details.      */
/*                                                                          */
/* ************************************************************************ */

package altdate;



enum DayType {
	ABSOULTE;
	FIRST;
	SECOND;
	THIRD;
	FOURTH;
	FIFTH;
	LAST;
	GE;
	LE;
}


class DaySpecification {
	var weekday : Int;	// Day of week (0 Sun -> 6 Sat) OR null
	var date : Int;		// date: date in month OR null
	var type : DayType;

	public function new() {
		weekday = 0;
		date = 1;
		type = ABSOULTE;
	}

	/**
		Get the proper date number for this specification,
		given a year and month (0-11)
	*/
	public function getValue(year:Int, month:Int) : Int {
		var rv : Int = 0;
		if(month < 0 || month > 11)
			throw "Month "+Std.string(month) + " is outside 0 - 11";

		var dates:Array<Int> = altdate.Doomsday.weekdates(year, month, weekday);
		switch(type) {
		case ABSOULTE:
			rv = date;
		case FIRST:
			rv = dates[0];
		case SECOND:
			rv = dates[1];
		case THIRD:
			rv = dates[2];
		case FOURTH:
			rv = dates[3];
		case FIFTH:
			if(dates.length < 5)
				throw "No fifth date found for year " + Std.string(year) + " month "+Std.string(month);
			rv = dates[4];
		case LAST:
			return dates.pop();
		case GE:
			for(i in dates) {
				if(i >= date)
					return i;
			}
			throw "No date found for year " + Std.string(year) + " month "+Std.string(month);
		case LE:
			for(i in dates) {
				if(i <= date)
					rv = i;
				if(i > date)
					break;
			}
			if(rv == 0)
				throw "No date found for year " + Std.string(year) + " month "+Std.string(month);

		}
		return rv;
	}

/**
	Parse a string of an arbitrary English style date.
	Formats are:
	[INT]	 an absoulte date value

	The following formats take a weekday argument, that being the
	3 letter weekday name [Sun,Mon,Tue, Wed,Thur,Fri,Sat].
	firstTue the first Tuesday of a month
	second
	third
	fourth
	fifth
	lastSun  the last Sunday in the month
	lastMon  the last Monday in the month

	Sun>=8   first Sunday on or after the eighth
	Sun<=25  last Sunday on or before the 25th
*/
	public static function parse(atom:String) : DaySpecification {
		//var r = ~/^([0-9]+)|(last([A-z]+))|(([A-z]+)([<>]=)([0-9]+))$/;

		var rv = new DaySpecification();
#if NO_EREG
		var isallnum : Bool = true;
		for(i in 0...atom.length) {
			var c = atom.charAt(i);
			if(	c!='0' && c !='1' && c!='2' &&
				c!='3' && c !='4' && c!='5' &&
				c!='6' && c !='7' && c!='8' &&
				c!='9'
				)
			{
				isallnum = false;
				break;
			}
		}
		if( isallnum ) {
			rv.type = ABSOULTE;
			rv.date = Std.parseInt(atom);
			return rv;
		}
#else
		var r = ~/^[0-9]+$/;
		if( r.match(atom) ) {
			rv.type = ABSOULTE;
			rv.date = Std.parseInt(atom);
			return rv;
		}
#end
		if(atom.charAt(0) == "f") {
			if(atom.substr(0,5) == "first") {
				rv.weekday = altdate.Tools.dayToInt(atom.substr(5));
				rv.type = FIRST;
				return rv;
			}
			if(atom.substr(0,6) == "fourth") {
				rv.weekday = altdate.Tools.dayToInt(atom.substr(6));
				rv.type = FOURTH;
				return rv;
			}
			if(atom.substr(0,5) == "fifth") {
				rv.weekday = altdate.Tools.dayToInt(atom.substr(5));
				rv.type = FIFTH;
				return rv;
			}
		}

		if(atom.substr(0,6) == "second") {
			rv.weekday = altdate.Tools.dayToInt(atom.substr(6));
			rv.type = SECOND;
			return rv;
		}
		if(atom.substr(0,5) == "third") {
			rv.weekday = altdate.Tools.dayToInt(atom.substr(5));
			rv.type = THIRD;
			return rv;
		}

		if(atom.substr(0,4) == "last") {
			rv.weekday = altdate.Tools.dayToInt(atom.substr(4));
			rv.type = LAST;
			return rv;
		}

		var p = atom.indexOf("=");
		if(p < 4)
			throw "Invalid TYPE spec [" + atom + "]";
		switch(atom.substr(p-1,1)) {
		case "<": rv.type = LE;
		case ">": rv.type = GE;
		default: throw "Invalid TYPE spec [" + atom + "] in equality operator";
		}
		rv.weekday = altdate.Tools.dayToInt(atom.substr(0,p-1));
		rv.date = Std.parseInt(atom.substr(p+1));
		return rv;
	}


}
